﻿//-----------------------------------------------------------------------
// <copyright file="MLObjectTreeExpand.cs" company="MAPILab Ltd">
//     Copyright (c) MAPILab Ltd.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace MAPILab.SharePoint.Explorer.ObjectBrowser.TreeExpand
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.DirectoryServices;
    using System.Drawing;
    using System.Reflection;
    using System.Text;
    using System.Windows.Forms;
    using MAPILab.SharePoint.Explorer.Utilities;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint.Upgrade;

    class MLObjectTreeExpand
    {
        #region Fields

        /// <summary>
        /// Tree node collection to add to expand object tree node
        /// </summary>
        private List<MLNodeInfo> treeNodeCollection = new List<MLNodeInfo>();

        /// <summary>
        /// List of temp object data (nodes to add to tree)
        /// </summary>
        private List<MLTempObjectData> sortList = new List<MLTempObjectData>();

        /// <summary>
        /// Rules for Enumerable objects to display items
        /// </summary>
        private static List<MLDisplayRule> rules = new List<MLDisplayRule>();

        #endregion

        #region Properties

        public static List<MLDisplayRule> Rules
        {
            get { return rules; }
            set { rules = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets object node  
        /// </summary>
        /// <param name="root">Is root node</param>
        /// <param name="name">Node name</param>
        /// <param name="nodeObject">Object behind the node</param>
        /// <param name="objectDeclatingType">Object type</param>
        /// <param name="publicProp">Is public</param>
        /// <param name="imageIndex">Image index</param>
        /// <param name="persistedObjectRole">Has persisted object role</param>
        /// <returns>tree node</returns>
        public static TreeNode GetObjectNode(bool root, string name, object nodeObject, Type objectDeclatingType, bool publicProp, ImageType imageIndex, bool persistedObjectRole)
        {
            string displayName = name;

            if (displayName.Length > 100)
            {
                displayName = displayName.Substring(0, 100) + "...";
            }

            TreeNode objectNode = new TreeNode(displayName);
            MLTreeTag tag = new MLTreeTag(nodeObject, objectDeclatingType);
            tag.PersistedObjectRole = persistedObjectRole;

            objectNode.Tag = tag;

            try
            {
                if (nodeObject is SPPersistedObject)
                {
                    imageIndex = ImageType.SPPersistedObject;
                }
                else if (nodeObject is Type)
                {
                    if ((nodeObject as Type).IsInterface)
                        imageIndex = ImageType.Interface;
                }
            }
            catch
            {
                if ((nodeObject as Type).IsInterface)
                    imageIndex = ImageType.SPPersistedObject;
            }

            if (nodeObject is IEnumerable && !(nodeObject is string))
            {
                objectNode.NodeFont = new Font(SystemFonts.DefaultFont, FontStyle.Bold);
            }

            if (!publicProp)
            {
                objectNode.ForeColor = System.Drawing.SystemColors.GrayText;
            }

            objectNode.ImageIndex = (int)imageIndex;
            objectNode.SelectedImageIndex = (int)imageIndex;

            return objectNode;
        }

        /// <summary>
        /// Gets root node
        /// </summary>
        /// <param name="o">object as a root node</param>
        /// <returns>node as root node</returns>
        public static TreeNode GetRootNode(object o)
        {
            if (o == null)
                o = new NotImplementedException("Empty node");

            string oName = o.ToString();

            if (o is DirectoryEntry)
            {
                try
                {
                    oName = (o as DirectoryEntry).Name;
                }
                catch
                {
                }
            }
            
            TreeNode objectNode = GetObjectNode(true, oName, o, o.GetType(), true, 0, false);

            return objectNode;
        }

        /// <summary>
        /// Returns objects collection for persisted child objects
        /// </summary>
        /// <param name="nodeObject">object node (SP persisted object)</param>
        /// <param name="nodePersistedChilds">node childs</param>
        public List<MLNodeInfo> UpdatePersistedChildObjects(object nodeObject, TreeNode nodePersistedChilds)
        {
            try
            {
                SPPersistedObject parentObject = nodeObject as SPPersistedObject;
                SPFarm localFarm = SPFarm.Local;

                MLSharePointSqlSession spSqlSession = new MLSharePointSqlSession(SPManager.Instance.ConnectionString.ConnectionString);

                string sqlQuery = string.Format("SELECT Id, Name, Properties FROM Objects WHERE ParentId = '{0}'", parentObject.Id.ToString());

                using (SqlDataReader reader = spSqlSession.ExecuteReader(new SqlCommand(sqlQuery)))
                {
                    sortList.Clear();

                    MLDisplayRule rule;
                    if (rules.Count > 0)
                        rule = rules[0];
                    else
                        rule = new MLDisplayRule();

                    while (reader.Read())
                    {
                        Guid objId = (Guid)reader[0];

                        SPPersistedObject childPersistedObject = localFarm.GetObject(objId);
                        if (childPersistedObject != null)
                        {
                            string objectName = GetObjectName(childPersistedObject, ref rule);

                            //InsertObject
                            sortList.Add(new MLTempObjectData(
                                                false,
                                                objectName, //childPersistedObject.ToString(),
                                                childPersistedObject,
                                                childPersistedObject.GetType(),
                                                true,
                                                ImageType.Class,
                                                false));
                        }
                        else
                        {
                            MLInvalidPersistedObject invalidObject = new MLInvalidPersistedObject(objId, (string)reader[1], (string)reader[2]);

                            //InsertObject
                            sortList.Add(new MLTempObjectData(
                                                false,
                                                invalidObject.ToString(),
                                                invalidObject,
                                                invalidObject.GetType(),
                                                true,
                                                ImageType.Class,
                                                false));
                        }
                    }

                    InsertSortList(nodePersistedChilds.Nodes, ref sortList, false);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return treeNodeCollection;
        }

        /// <summary>
        /// Returns list of nodes to add to selected node
        /// </summary>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="parentNodes">Parent nodes</param>
        public List<MLNodeInfo> UpdateNodeMembers(object nodeObject, TreeNodeCollection parentNodes)
        {
            if (nodeObject == null)
                return new List<MLNodeInfo>();

            //Processing Enumerable Nodes
            if (nodeObject is IEnumerable)
            {
                ProcessingEnumerableNode(nodeObject, parentNodes);
            }

            //Processing CollectionInfo
            if (nodeObject is MLCollectionInfo)
            {
                ProcessingCollectionNode(nodeObject, parentNodes);

                return treeNodeCollection;
            }
            //Processing SPListItems
            if (nodeObject is SPListItem)
            {
                ProcessingSPListItem(nodeObject, parentNodes);

                //return treeNodeCollection;
            }

            //Processing else items
            InsertProperties(parentNodes, nodeObject);
            InsertObjectConstructors(parentNodes, nodeObject);
            InsertObjectFields(parentNodes, nodeObject);
            InsertObjectMethods(parentNodes, nodeObject);

            TreeNode typeInformationNode = GetObjectNode(false, "Type Information", nodeObject.GetType(), nodeObject.GetType(), true, 0, false);
            ReportProgress(typeInformationNode, parentNodes, true);

            TreeNode implementedInterfacesNode = GetObjectNode(false, "Implemented Interfaces", nodeObject.GetType().GetInterfaces(), typeof(Type[]), true, ImageType.Interface, false);
            ReportProgress(implementedInterfacesNode, parentNodes, true);

            if (nodeObject is SPPersistedObject)
            {
                TreeNode node = GetObjectNode(false, "Persisted Children", nodeObject, nodeObject.GetType(), true, ImageType.SPPersistedObject, true);
                ReportProgress(node, parentNodes, true);
            }

            return treeNodeCollection;
        }

        /// <summary>
        /// Processing SPList Item
        /// </summary>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="parentNodes">Parent nodes</param>
        private void ProcessingSPListItem(object nodeObject, TreeNodeCollection parentNodes)
        {
            SPListItem listItem = nodeObject as SPListItem;

            TreeNode nodeFieldsItems = new TreeNode("Fields collection");
            System.Drawing.Font font = new System.Drawing.Font(
                System.Drawing.SystemFonts.DefaultFont,
                System.Drawing.FontStyle.Underline | System.Drawing.FontStyle.Bold);
            nodeFieldsItems.NodeFont = font;

            //parentNodes.Add(nodeFieldsItems);
            ReportProgress(nodeFieldsItems, parentNodes, false);

            sortList.Clear();

            try
            {
                foreach (SPField field in listItem.Fields)
                {
                    object rawValue = listItem[field.Id];
                    MLFieldNameValue fieldNameValue = new MLFieldNameValue(field, rawValue);

                    //InsertObject
                    sortList.Add(new MLTempObjectData(
                                        false,
                                        fieldNameValue.ToString(),
                                        fieldNameValue,
                                        fieldNameValue.GetType(),
                                        true,
                                        ImageType.Class,
                                        false));
                }
            }
            catch (Exception ex)
            {
                //InsertObject
                sortList.Add(new MLTempObjectData(
                                    false,
                                    ex.ToString(),
                                    ex,
                                    typeof(Exception),
                                    true,
                                    ImageType.Class,
                                    false));
            }

            InsertSortList(nodeFieldsItems.Nodes, ref sortList, true);
        }

        /// <summary>
        /// Processing Collection Node
        /// </summary>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="parentNodes">Parent nodes</param>
        private void ProcessingCollectionNode(object nodeObject, TreeNodeCollection parentNodes)
        {
            sortList.Clear();

            MLCollectionInfo collInfo = nodeObject as MLCollectionInfo;
            if (collInfo.Enumerable != null)
            {
                try
                {
                    IEnumerable enumerator = collInfo.Enumerable;

                    MLDisplayRule rule;
                    if (rules.Count > 0)
                        rule = rules[0];
                    else
                        rule = new MLDisplayRule();

                    foreach (object item in enumerator)
                    {
                        object currentItem = item;

                        //null
                        if (currentItem == null)
                        {
                            currentItem = new NullReferenceException();
                        }

                        string objectName = GetObjectName(currentItem, ref rule);

                        //InsertObject
                        sortList.Add(new MLTempObjectData(
                                            false,
                                            objectName,
                                            currentItem,
                                            currentItem.GetType(),
                                            true,
                                            ImageType.Class,
                                            false));
                    }
                }
                catch (Exception ex)
                {
                    //InsertObject
                    sortList.Add(new MLTempObjectData(
                                        false,
                                        ex.ToString(),
                                        ex,
                                        typeof(Exception),
                                        true,
                                        ImageType.Class,
                                        false));
                }

                InsertSortList(parentNodes,ref sortList, true);
                collInfo.ItemsCount = sortList.Count;
            }
        }

        /// <summary>
        /// Returns object name of collection node
        /// </summary>
        /// <param name="currentItem">IEnumerable object</param>
        /// <returns></returns>
        private static string GetObjectName(object currentItem, ref MLDisplayRule lastRule)
        {
            string objectName = String.Empty;
            string type = currentItem.GetType().ToString();
            bool isFound = false;

            if (type == lastRule.Type)
            {
                objectName = GetObjectNameUsingRule(currentItem, lastRule);
                isFound = true;
            }

            if (!isFound)
            {
                foreach (MLDisplayRule rule in rules)
                {
                    if (type == rule.Type)
                    {
                        objectName = GetObjectNameUsingRule(currentItem, rule);

                        lastRule = rule; 

                        isFound = true;

                        break;
                    }
                }
            }

            if (!isFound)
                objectName = currentItem.ToString();
            
            return objectName;
        }

        /// <summary>
        /// Get properties from object
        /// </summary>
        /// <param name="currentItem">The object to process</param>
        /// <param name="parts">Parts of the rule. The first Part is a type name, last one is empty. The other one are properties name</param>
        /// <returns>Object name made by rule</returns>
        private static string GetObjectNameUsingRule(object currentItem, MLDisplayRule rule)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(String.Format("{0}", rule.Name));

            if (rule.Properties.Count > 0)
                sb.Append(":");

            string value = String.Empty;

            for (int i = 0; i < rule.Properties.Count; i++)
            {
                MLRuleProperty property = rule.Properties[i];

                if (!property.Hidden)
                {
                    if (property.Type == MLRuleProperty.MLType.Property)
                    {
                        PropertyInfo prop = currentItem.GetType().GetProperty(property.Name);

                        if (prop != null)
                        {
                            value = prop.GetValue(currentItem, null).ToString();
                        }
                        else
                        {
                            prop = currentItem.GetType().GetProperty(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                            if (prop != null)
                            {
                                value = prop.GetValue(currentItem, null).ToString();
                                property.Hidden = true;
                            }
                            else
                            {
                                FieldInfo field = currentItem.GetType().GetField(property.Name);

                                if (field != null)
                                {
                                    value = field.GetValue(currentItem).ToString();
                                    property.Type = MLRuleProperty.MLType.Field;
                                }
                                else
                                {
                                    field = currentItem.GetType().GetField(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                                    if (field == null)
                                        continue;

                                    value = field.GetValue(currentItem).ToString();
                                    property.Type = MLRuleProperty.MLType.Field;
                                    property.Hidden = true;
                                }
                            }
                        }
                    }
                    else if (property.Type == MLRuleProperty.MLType.Field)
                    {
                        FieldInfo field = currentItem.GetType().GetField(property.Name);

                        if (field != null)
                        {
                            value = field.GetValue(currentItem).ToString();
                        }
                        else
                        {
                            field = currentItem.GetType().GetField(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                            if (field == null)
                                continue;

                            value = field.GetValue(currentItem).ToString();
                            property.Hidden = true;
                        }
                    }
                    else
                    {
                        MethodInfo method = currentItem.GetType().GetMethod(property.Name, new Type[] { });

                        if (method != null)
                        {
                            value = method.Invoke(currentItem, null).ToString();
                        }
                        else
                        {
                            method = currentItem.GetType().GetMethod(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                            if (method == null)
                                continue;

                            value = method.Invoke(currentItem, null).ToString();
                            property.Hidden = true;
                        }
                    }
                }
                else
                {
                    if (property.Type == MLRuleProperty.MLType.Property)
                    {
                        PropertyInfo prop = currentItem.GetType().GetProperty(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                        if (prop == null)
                            continue;

                        value = prop.GetValue(currentItem, null).ToString();
                    }
                    else if (property.Type == MLRuleProperty.MLType.Field)
                    {
                        FieldInfo field = currentItem.GetType().GetField(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                        if (field == null)
                            continue;

                        value = field.GetValue(currentItem).ToString();
                    }
                    else
                    {
                        MethodInfo method = currentItem.GetType().GetMethod(property.Name, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);

                        if (method == null)
                            continue;

                        value = method.Invoke(currentItem, null).ToString();
                    }
                }

                sb.Append(String.Format(" {0}={1};", property.Title, value));
            }

           return sb.ToString();
        }

        /// <summary>
        /// Processing Enumerable Node
        /// </summary>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="parentNodes">Parent nodes</param>
        private void ProcessingEnumerableNode(object nodeObject, TreeNodeCollection parentNodes)
        {
            TreeNode nodeCollItems = new TreeNode("Collection Items");
            nodeCollItems.NodeFont = new System.Drawing.Font(
                System.Drawing.SystemFonts.DefaultFont,
                System.Drawing.FontStyle.Underline | System.Drawing.FontStyle.Bold);

            int numberOfObjects = 0;
            try
            {
                foreach (object o in (IEnumerable)nodeObject)
                {
                    numberOfObjects++;
                }
            }
            catch
            {
                numberOfObjects = 1;
            }

            MLCollectionInfo collInfo = new MLCollectionInfo(numberOfObjects, (IEnumerable)nodeObject);
            nodeCollItems.Tag = new MLTreeTag(collInfo, collInfo.GetType(), false);

            bool hasNodes = true;
            if (numberOfObjects == 0)
                hasNodes = false;

            ReportProgress(nodeCollItems, parentNodes, hasNodes);
        }

        /// <summary>
        /// Insert property nodes
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        private void InsertProperties(TreeNodeCollection parentNodes, object nodeObject)
        {
            //Add public properties
            PropertyInfo[] propsInfo = nodeObject.GetType().GetProperties();
            if (propsInfo.Length > 0)
                InsertProperties(parentNodes, nodeObject, propsInfo, true);

            //Add non public properties 
            PropertyInfo[] nonPropsInfo = nodeObject.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (nonPropsInfo.Length > 0)
                InsertProperties(parentNodes, nodeObject, nonPropsInfo, false);
        }

        /// <summary>
        /// Insert public or non public property nodes
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="propertiesInfo">Properties info</param>
        /// <param name="publicProp">Is public propery</param>
        private void InsertProperties(TreeNodeCollection parentNodes, object nodeObject, PropertyInfo[] propertiesInfo, bool publicProp)
        {
            sortList.Clear();

            foreach (PropertyInfo propInfo in propertiesInfo)
            {
                ParameterInfo[] indexParameters = propInfo.GetIndexParameters();
                string propName = String.Empty;
                object propValue = null;

                ExtractPropNameAndValue(nodeObject, propInfo, indexParameters, publicProp, ref propValue, ref propName);

                //InsertObject
                sortList.Add(new MLTempObjectData(
                                    false,
                                    propName,
                                    propValue,
                                    propInfo.PropertyType,
                                    publicProp,
                                    ImageType.Property,
                                    false));
            }

            if (publicProp)
            {
                InsertSortList(parentNodes, ref sortList, false);
            }
            else
            {
                TreeNode nodeNonPublicProps = CreateFolderNode("Properties", ImageType.Property, parentNodes, propertiesInfo, publicProp);
                InsertSortList(nodeNonPublicProps.Nodes, ref sortList, false);
            }
        }

        /// <summary>
        /// Extract property name and value
        /// </summary>
        /// <param name="nodeObject">node</param>
        /// <param name="propInfo">property info</param>
        /// <param name="indexParameters">index parameters</param>
        /// <param name="propValue">returns property value</param>
        /// <param name="propName">returns propery name</param>
        private static void ExtractPropNameAndValue(object nodeObject, PropertyInfo propInfo, ParameterInfo[] indexParameters, bool publicProp, ref object propValue, ref string propName)
        {
            try
            {
                if (indexParameters.Length > 0)
                {
                    List<object> values = new List<object>();
                    bool succeed = true;
                    int index = 0;

                    while (succeed && index < 0)
                    {
                        try
                        {
                            object obj = propInfo.GetValue(nodeObject, new object[] { index++ });
                            values.Add(obj);
                        }
                        catch
                        {
                            succeed = false;
                        }
                    }

                    propValue = values.ToArray();

                    foreach (ParameterInfo parInfo in indexParameters)
                    {
                        if (propName.Length != 0)
                        {
                            propName += "; ";
                        }

                        propName += parInfo.ParameterType.Name + " " + parInfo.Name;
                    }

                    propName = string.Format("{0}[{1}]", propInfo.Name, propName);
                }
                else
                {
                    propName = propInfo.Name;

                    //Workaround to avoid stack overflow exception
                    if (publicProp || propName != "Folders" || !(nodeObject is SPFolder))
                    {
                        propValue = propInfo.GetValue(nodeObject, null);
                    }
                    else
                    {
                        propValue = new StackOverflowException("Workaround: Impossible to get property in order to avoid stack overflow");
                    }
                }
            }
            catch (Exception ex)
            {
                propValue = ex;
            }
        }

        /// <summary>
        /// Insert object constructors
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        private void InsertObjectConstructors(TreeNodeCollection parentNodes, object nodeObject)
        {
            //Add public constructors
            ConstructorInfo[] ctorsInfo = nodeObject.GetType().GetConstructors();
            if (ctorsInfo.Length > 0)
                InsertObjectConstructors(parentNodes, ctorsInfo, true);

            //Add non public constructors
            ConstructorInfo[] hiddenCtorsInfo = nodeObject.GetType().GetConstructors(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (hiddenCtorsInfo.Length > 0)
                InsertObjectConstructors(parentNodes, hiddenCtorsInfo, false);
        }

        /// <summary>
        /// Insert object public or non public constructors
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="ctorsInfo">Constructors info</param>
        /// <param name="publicCtors">Is public constructor</param>
        private void InsertObjectConstructors(TreeNodeCollection parentNodes, ConstructorInfo[] ctorsInfo, bool publicCtors)
        {
            sortList.Clear();

            foreach (ConstructorInfo ctor in ctorsInfo)
            {
                //InsertObject
                sortList.Add(new MLTempObjectData(
                                    false,
                                    ctor.Name,
                                    ctor,
                                    Type.Missing.GetType(),
                                    publicCtors,
                                    ImageType.Method,
                                    false));
            }

            TreeNode node = CreateFolderNode("Constructors", ImageType.Method, parentNodes, ctorsInfo, publicCtors);
            InsertSortList(node.Nodes, ref sortList, false);
        }

        /// <summary>
        /// Insert object fields
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        private void InsertObjectFields(TreeNodeCollection parentNodes, object nodeObject)
        {
            //Add public fields
            FieldInfo[] fieldsInfo = nodeObject.GetType().GetFields();
            if (fieldsInfo.Length > 0)
                InsertObjectFields(parentNodes, nodeObject, fieldsInfo, true);

            //Add non public fields
            FieldInfo[] hiddenFieldsInfo = nodeObject.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (hiddenFieldsInfo.Length > 0)
                InsertObjectFields(parentNodes, nodeObject, hiddenFieldsInfo, false);
        }

        /// <summary>
        /// Insert object public or non public fields
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        /// <param name="fieldsInfo">Fields info</param>
        /// <param name="publicField">Is public fields</param>
        private void InsertObjectFields(TreeNodeCollection parentNodes, object nodeObject, FieldInfo[] fieldsInfo, bool publicField)
        {
            sortList.Clear();

            foreach (FieldInfo fieldInfo in fieldsInfo)
            {
                object fieldValue;

                try
                {
                    fieldValue = fieldInfo.GetValue(nodeObject);
                }
                catch (Exception ex)
                {
                    fieldValue = ex;
                }

                //InsertObject
                sortList.Add(new MLTempObjectData(
                                    false,
                                    fieldInfo.Name,
                                    fieldValue,
                                    fieldInfo.FieldType,
                                    publicField,
                                    ImageType.Field,
                                    false));
            }

            TreeNode nodeFields = CreateFolderNode("Fields", ImageType.Field, parentNodes, fieldsInfo, publicField);
            InsertSortList(nodeFields.Nodes, ref sortList, false);
        }

        /// <summary>
        /// Insert object methods
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="nodeObject">Selected node</param>
        private void InsertObjectMethods(TreeNodeCollection parentNodes, object nodeObject)
        {
            //Add public methods
            MethodInfo[] methodsInfo = nodeObject.GetType().GetMethods();
            if (methodsInfo.Length > 0)
                InsertObjectMethods(parentNodes, methodsInfo, true);

            //Add non public methods
            MethodInfo[] hiddenMethodsInfo = nodeObject.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (hiddenMethodsInfo.Length > 0)
                InsertObjectMethods(parentNodes, hiddenMethodsInfo, false);
        }

        /// <summary>
        /// Insert object public or non public methods
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="methodsInfo">Methods info</param>
        /// <param name="publicMethods">Is public methods</param>
        private void InsertObjectMethods(TreeNodeCollection parentNodes, MethodInfo[] methodsInfo, bool publicMethods)
        {
            sortList.Clear();

            foreach (MethodInfo method in methodsInfo)
            {
                if (!(
                    method.Name.StartsWith("get_") ||
                    method.Name.StartsWith("set_") ||
                    method.Name.StartsWith("add_") ||
                    method.Name.StartsWith("remove_")))
                {
                    //InsertObject
                    sortList.Add(new MLTempObjectData(
                                        false,
                                        method.Name,
                                        method,
                                        method.ReturnType,
                                        publicMethods,
                                        ImageType.Method,
                                        false));
                }
            }

            TreeNode node = CreateFolderNode("Methods", ImageType.Method, parentNodes, methodsInfo, publicMethods);
            InsertSortList(node.Nodes, ref sortList, false);
        }

        /// <summary>
        /// Create folder node for properties, fields, ctors and etc.
        /// </summary>
        /// <param name="text">Folder name</param>
        /// <param name="imageIndex">Folder image</param>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="info">Info</param>
        /// <param name="isPublic">Is public folder</param>
        /// <returns>Folder tree node</returns>
        private TreeNode CreateFolderNode(string text, ImageType imageIndex, TreeNodeCollection parentNodes, object[] info, bool isPublic)
        {
            TreeNode node = new TreeNode();

            if (isPublic)
            {
                node.Text = text;
            }
            else
            {
                node.Text = "Hidden " + text;
                node.ForeColor = System.Drawing.SystemColors.GrayText;
            }

            node.ImageIndex = (int)imageIndex;
            node.SelectedImageIndex = (int)imageIndex;
            node.NodeFont = new Font(SystemFonts.DefaultFont, FontStyle.Underline);
            node.Tag = new MLTreeTag(info, info.GetType(), true);

            ReportProgress(node, parentNodes, false);

            return node;
        }

        /// <summary>
        /// Insert sort list to tree node collection
        /// </summary>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="sortList">Collection to add to object tree</param>
        /// <param name="noSort">Don't sort collection</param>
        private void InsertSortList(TreeNodeCollection parentNodes, ref List<MLTempObjectData> sortList, bool noSort)
        {
            if (!noSort)
            {
                sortList.Sort();
            }

            foreach (MLTempObjectData tod in sortList)
            {
                TreeNode node = GetObjectNode(
                                    tod.Root,
                                    tod.Name,
                                    tod.NodeObject,
                                    tod.ObjectDeclatingType,
                                    tod.PublicProp,
                                    tod.ImageIndex,
                                    tod.PersistedObjectRole);

                ReportProgress(node, parentNodes, true);
            }            
        }

        /// <summary>
        /// Add node to tree node collection
        /// </summary>
        /// <param name="node">Node to add</param>
        /// <param name="parentNodes">Parent nodes</param>
        /// <param name="hasNodes">This node has nodes</param>
        private void ReportProgress(TreeNode node, TreeNodeCollection parentNodes, bool hasNodes)
        {
            if (hasNodes)
                node.Nodes.Add("Loading...");

            treeNodeCollection.Add(new MLNodeInfo(node, parentNodes));
        }

        #endregion
    }
}
